using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content.Pipeline;
using Microsoft.Xna.Framework.Content.Pipeline.Graphics;
using Microsoft.Xna.Framework.Content.Pipeline.Processors;
using System.ComponentModel;

namespace Volume_Splitter
{
    static class Extensions
    {
        public static float Width(this BoundingBox self)
        {
            return self.Max.X - self.Min.X;
        }

        public static float Height(this BoundingBox self)
        {
            return self.Max.Y - self.Min.Y;
        }

        public static float Depth(this BoundingBox self)
        {
            return self.Max.Z - self.Min.Z;
        }

        //public static BoundingSphere BoundingSphere(this NodeContent self)
        //{
        //    return Microsoft.Xna.Framework.BoundingSphere.CreateFromPoints(self.Positions());
        //}

        public static IEnumerable<Vector3> Positions(this NodeContent self, float scale)
        {
            var children_positions = self.Children.SelectMany(child => Extensions.Positions(child, scale));
            if (self is MeshContent)
            {
                var mesh = self as MeshContent;
                var absoluteTransform = mesh.AbsoluteTransform;

                return children_positions.Concat(from p in mesh.Positions select Vector3.Transform(scale * p, absoluteTransform));
            }
            return children_positions;
        }
    }

    [ContentProcessor]
    class MyMaterialProcessor : MaterialProcessor
    {
        protected override ExternalReference<TextureContent> BuildTexture(string textureName,
            ExternalReference<TextureContent> texture, ContentProcessorContext context)
        {
            return context.BuildAsset<TextureContent,
                                      TextureContent>(texture, "TextureGammaProcessor");
        }
    }

    [ContentProcessor(DisplayName = "Volume Splitter Model Processor")]
    public class ContentProcessor1 : ModelProcessor
    {
        int h = 1;
        [DisplayName("Vertical splits")]
        [DefaultValue(1)]
        [Description("Number of vertical bounding spheres.")]
        public int H
        {
            get { return h; }
            set { h = value; }
        }

        int w = 3;
        [DisplayName("Horizontal splits")]
        [DefaultValue(3)]
        [Description("Number of horizontal bounding spheres.")]
        public int W
        {
            get { return w; }
            set { w = value; }
        }

        int d = 8;
        [DisplayName("Depth splits")]
        [DefaultValue(8)]
        [Description("Number of depth bounding spheres.")]
        public int D
        {
            get { return d; }
            set { d = value; }
        }

        public override ModelContent Process(NodeContent input, ContentProcessorContext context)
        {
            FlattenTransforms(input);
            var positions = (from p in input.Positions(base.Scale) select p).ToList();

            var bss = new List<BoundingSphere>();
            var bb = BoundingBox.CreateFromPoints(positions);
            for (int i = 0; i < w; i++)
                for (int j = 0; j < h; j++)
                    for (int l = 0; l < d; l++)
                    {
                        var x = bb.Min.X + bb.Width() * i / (float)w;
                        var y = bb.Min.Y + bb.Height() * j / (float)h;
                        var z = bb.Min.Z + bb.Depth() * l / (float)d;
                        var X = bb.Min.X + bb.Width() * (i + 1) / (float)w;
                        var Y = bb.Min.Y + bb.Height() * (j + 1) / (float)h;
                        var Z = bb.Min.Z + bb.Depth() * (l + 1) / (float)d;
                        var local_BB = new BoundingBox(new Vector3(x, y, z),
                                                       new Vector3(X, Y, Z));
                        var local_positions = (from p in positions
                                               where local_BB.Contains(p) != ContainmentType.Disjoint
                                               select p);
                        if (local_positions.Any())
                            bss.Add(BoundingSphere.CreateFromBoundingBox(local_BB));
                        //bss.Add(BoundingSphere.CreateFromPoints(local_positions));
                    }

            var res = base.Process(input, context);
            res.Tag = new Dictionary<string, System.Object>()
              {
                {"BoundingSphere", bss.Aggregate(BoundingSphere.CreateMerged)},
                {"SubVolumes", bss}
              };
            return res;
        }

        protected override MaterialContent ConvertMaterial(MaterialContent material,
                                                           ContentProcessorContext context)
        {
            return context.Convert<MaterialContent,
                                   MaterialContent>(material, "MyMaterialProcessor");
        }

        static void FlattenTransforms(NodeContent node)
        {
            MeshHelper.TransformScene(node, node.Transform);

            node.Transform = Matrix.Identity;

            foreach (NodeContent child in node.Children)
            {
                FlattenTransforms(child);
            }
        }
    }
}